/*
 * ActionFactory.java
 *
 * Copyright 2007, 2008 Tiberiumlabs
 *
 * This file is part of Tiberiumlabs Lailaps.
 *
 * Tiberiumlabs Lailaps is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Tiberiumlabs Lailaps is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.tiberiumlabs.lailaps.actions;

import java.util.WeakHashMap;
import javax.swing.AbstractAction;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.tiberiumlabs.lailaps.Mediator;
import org.tiberiumlabs.lailaps.actions.events.DataEvent;
import org.tiberiumlabs.lailaps.actions.list.AddElementAction;
import org.tiberiumlabs.lailaps.actions.list.EditElementAction;
import org.tiberiumlabs.lailaps.actions.list.PauseDownloadAction;
import org.tiberiumlabs.lailaps.actions.list.RemoveCompletedAction;
import org.tiberiumlabs.lailaps.actions.list.RemoveElementAction;
import org.tiberiumlabs.lailaps.actions.list.SaveListAction;
import org.tiberiumlabs.lailaps.actions.list.SaveListAsAction;
import org.tiberiumlabs.lailaps.actions.list.StartAllAction;
import org.tiberiumlabs.lailaps.actions.list.StartDownloadAction;
import org.tiberiumlabs.lailaps.actions.list.StopAllAction;
import org.tiberiumlabs.lailaps.actions.list.StopDownloadAction;
import org.tiberiumlabs.lailaps.actions.system.AbstractShowWindowAction;
import org.tiberiumlabs.lailaps.actions.system.ChangeSettingsAction;
import org.tiberiumlabs.lailaps.actions.system.ExitAction;
import org.tiberiumlabs.lailaps.actions.system.ShowDropTargetAction;
import org.tiberiumlabs.lailaps.actions.system.ShowMainWindowAction;
import org.tiberiumlabs.lailaps.gui.elements.ExceptionDialog;
import org.tiberiumlabs.lailaps.list.DownloadItem;
import org.tiberiumlabs.lailaps.list.DownloadsListModel;
import org.tiberiumlabs.lailaps.settings.Settings;
import org.tiberiumlabs.lailaps.threads.ThreadsMap;
import org.tiberiumlabs.lailaps.toolkit.StateManager;
import org.tiberiumlabs.util.BackgroundWorker;

/**
 *
 * @author <a href="mailto:paranoid.tiberiumlabs@gmail.com">Paranoid</a>
 */
public abstract class ActionFactory {

// constants for private usage:

    private static final Mediator MEDIATOR = Mediator.getMediator();
    private static final WeakHashMap<String, AbstractAction> actionsMap = new WeakHashMap<String, AbstractAction>();
    private static final DownloadsListModel model = MEDIATOR.getDownloadsListModel();
    private static final Log log = LogFactory.getLog(ActionFactory.class);
    private static final ThreadsMap threadsMap = MEDIATOR.getThreadsMap();
    private static final StateManager stateManager = MEDIATOR.getStateManager();
    private static BackgroundWorker<Runnable> executor = null;

    public static final void execute(Runnable event) {
        if (executor == null) {
            executor = new BackgroundWorker<Runnable>() {
                @Override
                public void doInBackground(Runnable e) throws Throwable {
                    e.run();
                }

                @Override
                public void processException(Runnable e, Throwable th) {
                    log.warn(e, th);
                }
                
            };
            executor.setName("Lailaps-action-executor");
            executor.setPriority(Thread.NORM_PRIORITY-1);
            executor.setDaemon(false);
            executor.setLazy(true);
            executor.start();
            log.info("[ ACTIONS ] new action executor created...");
        }
        try {
            //executor.execute(event);
            executor.add(event);
        } catch (Exception ex) {
            new ExceptionDialog(ex);
        }
    }
    public static final void shutdown() {
        if (executor != null) { // may be we even did not start it?
            executor.shutdownAndWait();
            log.info("[ ACTIONS ] executor shutdowned...");
        }
    }

    // <editor-fold defaultstate="collapsed" desc=" keys for actions ">
    private static final String K_LIST_SAVEAS = "list_save_as";
    private static final String K_LIST_SAVE = "list_save";
    private static final String K_ELEMENT_REMOVE = "element_remove";
    private static final String K_ELEMENT_EDIT = "element_edit";
    private static final String K_ELEMENT_ADD = "element_add";
    private static final String K_SETTINGS_DIALOG = "settings_dialog";
    private static final String K_ELEMENT_START = "element_start";
    private static final String K_ELEMENT_PAUSE = "element_pause";
    private static final String K_ELEMENT_STOP = "element_stop";
    private static final String K_ALL_START = "element_start_all";
    private static final String K_ALL_STOP = "element_stop_all";
    private static final String K_REMOVE_COMPLETED = "remove_completed";
    private static final String K_SHOW_MAIN = "show_main_window";
    private static final String K_SHOW_DROPTARGET = "show_drop_target";
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" action factory methods ">

    public AbstractAction createSaveListAsAction() {
        AbstractAction result = actionsMap.get(K_LIST_SAVEAS);
        if (result == null) {
            result = new SaveListAsAction();
            actionsMap.put(K_LIST_SAVEAS, result);
        }
        return result;
    }

    public AbstractAction createSaveListAction() {
        AbstractAction result = actionsMap.get(K_LIST_SAVE);
        if (result == null) {
            result = new SaveListAction();
            actionsMap.put(K_LIST_SAVE, result);
        }
        return result;
    }

    public AbstractAction createRemoveElementAction() {
        AbstractAction result = actionsMap.get(K_ELEMENT_REMOVE);
        if (result == null) {
            result = new RemoveElementAction();
            actionsMap.put(K_ELEMENT_REMOVE, result);
        }
        return result;
    }

    public AbstractAction createEditElementAction() {
        AbstractAction result = actionsMap.get(K_ELEMENT_EDIT);
        if (result == null) {
            result = new EditElementAction();
            actionsMap.put(K_ELEMENT_EDIT, result);
        }
        return result;
    }

    public AbstractAction createAddElementAction() {
        AbstractAction result = actionsMap.get(K_ELEMENT_ADD);
        if (result == null) {
            result = new AddElementAction();
            actionsMap.put(K_ELEMENT_ADD, result);
        }
        return result;
    }

    public AbstractAction createSettingsDialogAction() {
        AbstractAction result = actionsMap.get(K_SETTINGS_DIALOG);
        if (result == null) {
            result = new ChangeSettingsAction();
            actionsMap.put(K_SETTINGS_DIALOG, result);
        }
        return result;
    }

    public AbstractAction createStartAction() {
        AbstractAction result = actionsMap.get(K_ELEMENT_START);
        if (result == null) {
            result = new StartDownloadAction();
            actionsMap.put(K_ELEMENT_START, result);
        }
        return result;
    }
    public AbstractAction createStopAction() {
        AbstractAction result = actionsMap.get(K_ELEMENT_STOP);
        if (result == null) {
            result = new StopDownloadAction();
            actionsMap.put(K_ELEMENT_STOP, result);
        }
        return result;
    }
    public AbstractAction createPauseAction() {
        AbstractAction result = actionsMap.get(K_ELEMENT_PAUSE);
        if (result == null) {
            result = new PauseDownloadAction();
            actionsMap.put(K_ELEMENT_PAUSE, result);
        }
        return result;
    }

    public AbstractAction createStartAllAction() {
        AbstractAction result = actionsMap.get(K_ALL_START);
        if (result == null) {
            result = new StartAllAction();
            actionsMap.put(K_ALL_START, result);
        }
        return result;
    }
    public AbstractAction createStopAllAction() {
        AbstractAction result = actionsMap.get(K_ALL_STOP);
        if (result == null) {
            result = new StopAllAction();
            actionsMap.put(K_ALL_STOP, result);
        }
        return result;
    }
    public AbstractAction createRemoveCompletedAction() {
        AbstractAction result = actionsMap.get(K_REMOVE_COMPLETED);
        if (result == null) {
            result = new RemoveCompletedAction();
            actionsMap.put(K_REMOVE_COMPLETED, result);
        }
        return result;
    }

    public AbstractShowWindowAction createShowMainWindowAction() {
        AbstractShowWindowAction showMainWindowAction = (AbstractShowWindowAction)actionsMap.get(K_SHOW_MAIN);
        if (showMainWindowAction == null) {
            showMainWindowAction = new ShowMainWindowAction();
            actionsMap.put(K_SHOW_MAIN, showMainWindowAction);
        }
        return showMainWindowAction;
    }

    public AbstractShowWindowAction createShowDropTargetAction() {
        AbstractShowWindowAction showDropTargetAction = (AbstractShowWindowAction)actionsMap.get(K_SHOW_DROPTARGET);
        if (showDropTargetAction == null) {
            showDropTargetAction = new ShowDropTargetAction();
            actionsMap.put(K_SHOW_DROPTARGET, showDropTargetAction);
        }
        return showDropTargetAction;
    }

    private ExitAction exitAction = null;
    public ExitAction createExitAction() {
        if (exitAction == null) {
            exitAction = new ExitAction();
        }
        return exitAction;
    }

    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc=" static actions ">

    public static final void silentAddElement(final DownloadItem item, final int position, final boolean autoStart) {
        execute(new Runnable() {
            @Override
            public void run() {
                int row = -1;
                if (position < 0) {
                    row = model.addItem(item);
                }
                else {
                    row = model.insertItem(position, item);
                }
                if (row >= 0) {

                    threadsMap.notify(new DataEvent(DataEvent.INSERTED, row, row, 1));

                    if (autoStart) {
                        threadsMap.create(item).start(row);
                        stateManager.add(StateManager.WORKING, 1);
                    } else {
                        stateManager.add(StateManager.STOPPED, 1);
                    }

                }
            }
        });
    }

    private static final void silentRemoveElement(final DownloadItem item) {
        execute(new Runnable() {
            @Override
            public void run() {
                int row = model.removeItem(item);
                if (row >= 0) {
                    threadsMap.notify(new DataEvent(DataEvent.REMOVED, row, row, 1));
                    stateManager.remove(StateManager.COMPLETED, 1);
                }
            }
        });
    }

    public static final void done(DownloadItem item, boolean withError) {
        threadsMap.done(item);
        if (withError) {
            stateManager.change(StateManager.WORKING, StateManager.STOPPED, 1);
            return;
        }
        if (Settings.getApplicationSettings().isRemoveFinished()) {
            silentRemoveElement(item);
        }
        stateManager.change(StateManager.WORKING, StateManager.COMPLETED, 1);
    }

    // </editor-fold>

}
